﻿using System.Runtime.CompilerServices;
using QuantBox.XApi;
using SmartQuant;
using ExecType = SmartQuant.ExecType;
using OrderSide = SmartQuant.OrderSide;

namespace QuantBox
{
    public class DualPosition
    {
        private const string LongDirection = "Long";
        private const string ShortDirection = "Short";

        public readonly Instrument Instrument;
        public PositionRecord Long;
        public PositionRecord Short;

        public DualPosition(string strategyName, Instrument instrument)
        {
            Instrument = instrument;
            Long = new PositionRecord(strategyName, instrument.Symbol, LongDirection);
            Short = new PositionRecord(strategyName, instrument.Symbol, ShortDirection);
        }

        public double LongTotal => Long.Qty + Long.FrozenOpen - Long.UnfrozenOpen;
        public double ShortTotal => Short.Qty + Short.FrozenOpen - Short.UnfrozenOpen;

        public double LongQty => Long.Qty;
        public double ShortQty => Short.Qty;
        public double NetQty => Long.Qty - Short.Qty;
        
        public double ShortYesterdayAvailable => ShortYesterday - ShortFrozenYesterday;
        public double LongYesterdayAvailable => LongYesterday - LongFrozenYesterday;
        
        public double ShortTodayAvailable => Short.QtyToday - Short.FrozenCloseToday + Short.UnfrozenCloseToday;
        public double LongTodayAvailable => Long.QtyToday - Long.FrozenCloseToday + Long.UnfrozenCloseToday;

        public double ShortAvailable => Short.Qty - Short.FrozenClose + Short.UnfrozenClose;
        public double LongAvailable => Long.Qty - Long.FrozenClose + Long.UnfrozenClose;

        public double ShortYesterday => Short.Qty - Short.QtyToday;
        public double LongYesterday => Long.Qty - Long.QtyToday;

        public double ShortToday => Short.QtyToday;
        public double LongToday => Long.QtyToday;

        public double ShortFrozenClose => Short.FrozenClose - Short.UnfrozenClose;
        public double LongFrozenClose => Long.FrozenClose - Long.UnfrozenClose;

        public double ShortFrozenYesterday => ShortFrozenClose - ShortFrozenToday;
        public double LongFrozenYesterday => LongFrozenClose - LongFrozenToday;

        public double ShortFrozenToday => Short.FrozenCloseToday - Short.UnfrozenCloseToday;
        public double LongFrozenToday => Long.FrozenCloseToday - Long.UnfrozenCloseToday;
        
        public double ShortHoldingCost => Short.HoldingCost;
        public double LongHoldingCost => Long.HoldingCost;

        public void ChangeTradingDay()
        {
            Long.ChangeTradingDay();
            Short.ChangeTradingDay();
        }

        public void SetPosition(PositionRecord position)
        {
            if (position.Direction == LongDirection)
            {
                Long = position;
            }
            else
            {
                Short = position;
            }
        }

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public PositionRecord GetPosition(OrderSide side, OrderOffsetFlag offsetFlag)
        {
            return offsetFlag.IsOpen
                ? side == OrderSide.Buy ? Long : Short
                : side == OrderSide.Buy ? Short : Long;
        }

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        private static OrderOffsetFlag GetOrderFlags(Order order)
        {
            var openCloseType = order.GetOpenClose();
            return new OrderOffsetFlag(openCloseType == OpenCloseType.Open, openCloseType == OpenCloseType.CloseToday);
        }

        #region Process Report

        public PositionRecord? ProcessExecutionReport(ExecutionReport report)
        {
            switch (report.ExecType)
            {
                case ExecType.ExecRejected:
                case ExecType.ExecCancelled:
                    return OnOrderRejected(report.Order);
                case ExecType.ExecTrade:
                    return OnOrderFilled(report);
            }

            return null;
        }

        public PositionRecord OnOrderRejected(Order order)
        {
            var offsetFlag = GetOrderFlags(order);
            var record = GetPosition(order.Side, offsetFlag);
            if (order.IsCancelled)
            {
                record.CumCancelCount++;
            }
            var leavesQty = order.LeavesQty;
            if (offsetFlag.IsOpen)
            {
                record.OrderRejectedOpen(leavesQty);
            }
            else if (offsetFlag.IsCloseToday)
            {
                record.OrderRejectedCloseToday(leavesQty);
            }
            else
            {
                record.OrderRejectedClose(leavesQty);
            }

            return record;
        }

        public PositionRecord FrozenPosition(Order order)
        {
            var offsetFlag = GetOrderFlags(order);
            var record = GetPosition(order.Side, offsetFlag);
            if (offsetFlag.IsOpen)
            {
                record.NewOrderOpen(order.LeavesQty);
            }
            else if (offsetFlag.IsCloseToday)
            {
                record.NewOrderCloseToday(order.LeavesQty);
            }
            else
            {
                record.NewOrderClose(order.LeavesQty);
            }

            return record;
        }

        public PositionRecord OnOrderFilled(ExecutionReport report)
        {
            var offsetFlag = GetOrderFlags(report.Order);
            var record = GetPosition(report.Side, offsetFlag);

            var lastQty = report.LastQty;
            var lastPrice = report.LastPx;

            if (offsetFlag.IsOpen)
            {
                record.OpenFilled(lastQty, lastPrice);
            }
            else if (offsetFlag.IsCloseToday)
            {
                record.CloseTodayFilled(lastQty, lastPrice);
            }
            else
            {
                record.CloseFilled(lastQty, lastPrice);
            }
            return record;
        }
        #endregion
    }
}
